package com.zdzx.base.common.util;

import java.security.Key;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.crypto.MacProvider;

public class JwtUtil {
	
	public static final SignatureAlgorithm JWT_ALG = SignatureAlgorithm.HS256;
	
	public static final String JWT_RULE = "com.zdzxtoken";

	public static final Integer JWT_DURATION = 60 * 60 * 5; // 秒 (12小时)

	/**
	 * 使用JWT默认方式，生成加解密密钥
	 *
	 * @param alg
	 *            加解密类型
	 * @return
	 */
	public static SecretKey generateKey(SignatureAlgorithm alg) {
		return MacProvider.generateKey(alg);
	}

	/**
	 * 使用指定密钥生成规则，生成JWT加解密密钥
	 *
	 * @param alg
	 *            加解密类型
	 * @param rule
	 *            密钥生成规则
	 * @return
	 */
	public static SecretKey generateKey(SignatureAlgorithm alg, String rule) {
		byte[] bytes = Base64.decodeBase64(rule);
		return new SecretKeySpec(bytes, alg.getJcaName());
	}

	/**
	 * 构建JWT
	 *
	 * @param alg
	 *            jwt 加密算法
	 * @param key
	 *            jwt 加密密钥
	 * @param sub
	 *            jwt 面向的用户
	 * @param aud
	 *            jwt 接收方
	 * @param jti
	 *            jwt 唯一身份标识
	 * @param iss
	 *            jwt 签发者
	 * @param nbf
	 *            jwt 生效日期时间
	 * @param duration
	 *            jwt 有效时间，单位：秒
	 * @return JWT字符串
	 */
	public static String buildJWT(SignatureAlgorithm alg, Key key, String sub, String aud, String jti, String iss,
			LocalDateTime nbf, Integer duration) {
		LocalDateTime iat = LocalDateTime.now();
		LocalDateTime exp = null;
		if (duration != null)
			exp = (nbf == null ? iat.plusSeconds(duration) : nbf.plusSeconds(duration));
		Date newNbf = nbf!=null?Date.from( nbf.atZone( ZoneId.systemDefault()).toInstant()):null;
		Date newIat = iat!=null?Date.from( iat.atZone( ZoneId.systemDefault()).toInstant()):null;
		Date newExp = exp!=null?Date.from( exp.atZone( ZoneId.systemDefault()).toInstant()):null;
		String compact = Jwts.builder().signWith(alg, key).setSubject(sub).setAudience(aud).setId(jti).setIssuer(iss)
				.setNotBefore(newNbf).setIssuedAt(newIat).setExpiration(exp != null ? newExp : null).compact();
		return compact;
	}

	/**
	 * 构建JWT
	 *
	 * @param sub
	 *            jwt 面向的用户
	 * @param aud
	 *            jwt 接收方
	 * @param jti
	 *            jwt 唯一身份标识
	 * @param iss
	 *            jwt 签发者
	 * @param nbf
	 *            jwt 生效日期时间
	 * @param duration
	 *            jwt 有效时间，单位：秒
	 * @return JWT字符串
	 */
	public static String buildJWT(String sub, String aud, String jti, String iss, LocalDateTime nbf, Integer duration) {
		return buildJWT(JWT_ALG, generateKey(JWT_ALG, JWT_RULE), sub, aud, jti, iss, nbf, duration);
	}

	/**
	 * 构建JWT
	 *
	 * @param sub
	 *            jwt 面向的用户
	 * @param jti
	 *            jwt 唯一身份标识，主要用来作为一次性token,从而回避重放攻击
	 * @return JWT字符串
	 */
	public static String buildJWT(String sub, String jti, Integer duration) {
		return buildJWT(sub, null, jti, null, null, duration);
	}

	/**
	 * 构建JWT
	 * <p>
	 * 使用 UUID 作为 jti 唯一身份标识
	 * </p>
	 * <p>
	 * JWT有效时间 600 秒，即 10 分钟
	 * </p>
	 *
	 * @param sub
	 *            jwt 面向的用户
	 * @return JWT字符串
	 */
	public static String buildJWT(String sub) {
		return buildJWT(sub, null, UUID.randomUUID().toString(), null, null, JWT_DURATION);
	}

	/**
	 * 解析JWT
	 *
	 * @param key
	 *            jwt 加密密钥
	 * @param claimsJws
	 *            jwt 内容文本
	 * @return {@link Jws}
	 * @throws Exception
	 */
	public static Map<String, Object> parseJWT(Key key, String claimsJws) {
		Map<String, Object> result = new HashMap<String, Object>();
		Jws<Claims> parseClaimsJws = null;
		String msg = "";
		try {
			parseClaimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(claimsJws);
			if (parseClaimsJws.getBody() == null) {
				result.put("result", false);
				result.put("code", JwtCode.TOKEN_CHECK);
			} else {
				result.put("result", true);
				result.put("code", parseClaimsJws);
			}
		} catch (SignatureException e) { // 签名异常
			msg = "签名异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_SIGNATURE);
		} catch (MalformedJwtException e) { // JWT格式错误
			msg = "JWT格式错误";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_MALFORMEDJWT);
		} catch (ExpiredJwtException e) { // JWT过期
			msg = "JWT过期";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_EXPIREDJWT);
			//result.put("jws", parseClaimsJws);
		} catch (UnsupportedJwtException e) { // 不支持该JWT
			msg = "不支持该JWT";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_UNSUPPORTEDJWT);
		} catch (IllegalArgumentException e) { // 参数错误异常
			msg = "参数错误异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ILLEGALARGUMENT);
		} catch (Exception e) {
			msg = "未知异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ALL);
		}
		System.out.println("JWT结果：" + msg);
		return result;
	}

	/**
	 * 校验JWT
	 *
	 * @param claimsJws
	 *            jwt 内容文本
	 * @return ture or false
	 */
	public static Map<String, Object> checkJWT(String claimsJws) {
		Map<String, Object> result = new HashMap<String, Object>();
		String msg = "";
		try {
			SecretKey key = generateKey(JWT_ALG, JWT_RULE);
			return parseJWT(key, claimsJws);
		} catch (SignatureException e) { // 签名异常
			msg = "签名异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_SIGNATURE);
		} catch (MalformedJwtException e) { // JWT格式错误
			msg = "JWT格式错误";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_MALFORMEDJWT);
		} catch (ExpiredJwtException e) { // JWT过期
			msg = "JWT过期";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_EXPIREDJWT);
		} catch (UnsupportedJwtException e) { // 不支持该JWT
			msg = "不支持该JWT";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_UNSUPPORTEDJWT);
		} catch (IllegalArgumentException e) { // 参数错误异常
			msg = "参数错误异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ILLEGALARGUMENT);
		} catch (Exception e) {
			msg = "未知异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ALL);
		}
		System.out.println("JTW校验结果：" + msg);
		return result;
	}

	/**
	 * 校验JWT
	 *
	 * @param key
	 *            jwt 加密密钥
	 * @param claimsJws
	 *            jwt 内容文本
	 * @param sub
	 *            jwt 面向的用户
	 * @return ture or false
	 */
	public static Map<String, Object> checkJWT(Key key, String claimsJws, String sub) {
		Map<String, Object> result = new HashMap<String, Object>();
		boolean flag = false;
		String msg = "";
		try {
			Map<String, Object> map = parseJWT(key, claimsJws);
			if ((boolean) map.get("result")) {
				return map;
			}
			Claims claims = (Claims) map.get("code");
			flag = claims.getSubject().equals(sub);
			if (flag) {
				result.put("result", true);
			} else {
				result.put("result", false);
				result.put("code", JwtCode.TOKEN_CHECK);
			}
		} catch (SignatureException e) { // 签名异常
			msg = "签名异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_SIGNATURE);
		} catch (MalformedJwtException e) { // JWT格式错误
			msg = "JWT格式错误";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_MALFORMEDJWT);
		} catch (ExpiredJwtException e) { // JWT过期
			msg = "JWT过期";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_EXPIREDJWT);
		} catch (UnsupportedJwtException e) { // 不支持该JWT
			msg = "不支持该JWT";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_UNSUPPORTEDJWT);
		} catch (IllegalArgumentException e) { // 参数错误异常
			msg = "参数错误异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ILLEGALARGUMENT);
		} catch (Exception e) {
			msg = "未知异常";
			result.put("result", false);
			result.put("code", JwtCode.TOKEN_ALL);
		}
		System.out.println("JTW校验结果：" + msg);
		return result;
	}

	/**
	 * 校验JWT
	 *
	 * @param claimsJws
	 *            jwt 内容文本
	 * @param sub
	 *            jwt 面向的用户
	 * @return ture or false
	 */
	public static Map<String, Object> checkJWT(String claimsJws, String sub) {
		return checkJWT(generateKey(JWT_ALG, JWT_RULE), claimsJws, sub);
	}

	@SuppressWarnings("unchecked")
	public static TokenInfo getTokenInfo(String token) {
		Jws<Claims> parseJWT = null;
		Map<String, Object> map = JwtUtil.parseJWT(JwtUtil.generateKey(JwtUtil.JWT_ALG, JwtUtil.JWT_RULE), token);
		if ((boolean) map.get("result")) {
			parseJWT = (Jws<Claims>) map.get("code");
		}
		TokenInfo tInfo=null;
		if (parseJWT != null) {
			String subject = parseJWT.getBody().getSubject();
			String id = parseJWT.getBody().getId();
			tInfo=new TokenInfo(id,subject);
		} 
		return tInfo;
	}
	
	public static TokenInfo parseExpriToken(String token) {
			try {
				Jws<Claims> parse = Jwts.parser().setSigningKey(JwtUtil.generateKey(JwtUtil.JWT_ALG, JwtUtil.JWT_RULE)).parseClaimsJws(token);
			if(parse!=null&&parse.getBody()!=null) {
				String id = parse.getBody().getId();
				String subject = parse.getBody().getSubject();
				return new TokenInfo(id, subject);
			}
			return null;
			}catch (ExpiredJwtException e) {
				 String subject = e.getClaims().getSubject();
				 String id = e.getClaims().getId();
				return new TokenInfo(id, subject);
	         }
		
	}

	
}